-
Notifications
You must be signed in to change notification settings - Fork 687
DATACMNS-1034 - Introduced API to easily register bidirectional, type-based converters. #209
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…va 8 lambdas. Introduced ConverterBuilder which exposes API to register Spring GenericConverters for the use with a store module's CustomConversions. This allows simple registration of such converters using Java 8 lambdas. ConverterAware converters = ConverterBuilder.reading(String.class, Long.class, it -> Long.valueOf(it)).andWriting(it -> Object::toString); The setup can also be done from the reading side which would just need the method invocations inverted. The resulting ConverterAware will expose the registered converters so that they can be easily handed to a CustomConversions instance. Partial creation of either reading or writing converters is possible, too with the returned instance then only exposing one of the two converters.
readme.md
Outdated
@@ -31,7 +31,7 @@ This README as well as the [reference documentation](http://docs.spring.io/sprin | |||
|
|||
The main project [website](http://projects.spring.io/spring-data/) contains links to basic project information such as source code, JavaDocs, Issue tracking, etc. | |||
|
|||
For more detailed questions, please refer to [spring-data on stackoverflow](http://stackoverflow.com/questions/tagged/spring-data). If you are new to Spring as well as to Spring Data, look for information about [Spring projects](https://spring.io/projects). | |||
For more detailed questions, please refer to [spring-data on stackoverflow](http://stackoverflow.com/questions/tagged/spring-data). If you are new to Spring as well as to Spring Data, look for information about [Spring projects](https://spring.io/projects). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not the place to suggest moving to asciidoctor?
* @return | ||
*/ | ||
static <S, T> ReadingConverterBuilder<T, S> reading(Class<T> source, Class<S> target, | ||
Function<? super T, ? extends S> function) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Class<T> source
looks backward (S is source, right?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, I flipped those.
* @since 2.0 | ||
*/ | ||
interface ReadingConverterBuilder<T, S> extends ConverterBuilder, ReadingConverterAware { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be <S, T>
?
|
||
/** | ||
* A {@link ConverterBuilder} ware of both a reading and writing converter. | ||
* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
aware
@Override | ||
public Set<GenericConverter> getConverters() { | ||
return Optionals.toStream(getOptionalReadingConverter(), getOptionalWritingConverter()).collect(Collectors.toSet()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it's just me, but I almost prefer reading this Java 8 stream as..
return Optionals
.toStream(...)
.collect(...)
Even if this results in an extra blank line to comply. ¯\_(ツ)_/¯
private static class Writing<S, T> extends ConfigurableGenericConverter<S, T> { | ||
|
||
public Writing(ConvertiblePair convertiblePair, Function<? super S, ? extends T> function) { | ||
super(convertiblePair, function); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is public visibility modifier required?
private static class Reading<S, T> extends ConfigurableGenericConverter<S, T> { | ||
|
||
public Reading(ConvertiblePair convertiblePair, Function<? super S, ? extends T> function) { | ||
super(convertiblePair, function); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Public required?
|
||
/** | ||
* Creates a {@link PagedResources} with an empt collection {@link EmbeddedWrapper} for the given domain type. | ||
* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
empty
} | ||
|
||
/** | ||
* Creates a {@link PagedResources} with an empt collection {@link EmbeddedWrapper} for the given domain type. | ||
* | ||
* @param page must not be {@literal null}, content must be empty. | ||
* @param type must not be {@literal null}. | ||
* @param link can be {@literal null}. | ||
* @param link must not be {@literal null}. | ||
* @return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this actually the place for specify this, given the assertion isn't here?
private static void assertOnlyConverter(ConverterBuilder builder, Supplier<GenericConverter> supplier) { | ||
assertThat(builder.getConverters()).containsExactly(supplier.get()); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test case is simply beautiful.
Somehow some bogus commits slipped into this one. Removing. |
Made nested API interfaces public so that they can actually be used. CustomConversions now considers ConverterAware and treats them appropriately for ConvertiblePair registration as well as during the registration in the ConversionService. Tweaked parameter types in CustomConversions to rather accept a Collection<?> for the converters (previously List<?>). Also, registerConverterIn(…) now takes a ConverterRegistry over a GenericConversionService. Polished Javadoc and non-null assertions.
3f73bc7
to
3436c63
Compare
Tweaked code according to @gregturn's suggestions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some comments about minor issues and thoughts. LGTM, besides some typos.
* @author Oliver Gierke | ||
* @since 2.0 | ||
*/ | ||
public interface ConverterAware extends ConverterBuilder, ReadingConverterAware, WritingConverterAware {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ConverterBuilder
implies mutability whereas ConverterAware
is the final result of the builder. It reads a bit strange.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Naming suggestions? ;)
* @author Oliver Gierke | ||
* @since 2.0 | ||
*/ | ||
public interface WritingConverterAware { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public
not required. All nested types of in an interface inherit public
visibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting, I though at some point, Eclipse presented them as package protected so I thought the default of public only applied to methods. Rolling this back then.
conversionService.addConverter(Converter.class.cast(it)); | ||
added = true; | ||
} | ||
boolean added = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found it easier to read (understand) multiple return
's instead of setting the added
state:
if (candidate instanceof Converter) {
conversionService.addConverter(Converter.class.cast(candidate));
return;
}
so throw new IllegalArgumentException(…)
would be the last and unconditional statement in the method body. Does this make sense?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At a quick glance, yes. Not at a second. The approach used allows the instance to implement multiple interfaces and thus obtains all ConvertibleType
instances that implementing multiple ones would expose. An early return would stop that process after the first step.
Not sure this is still needed as I am unsure how likely this is in the first place. I just kept it around to preserve behavioural consistency to the old CustomConversions
. We could think about removing that functionality, though.
|
||
/** | ||
* API to easily set up {@link GenericConverter} instances using Java 8 lambdas, mostly in bidirectional fashion for | ||
* easy registration as custom type converters of the SPring Data mapping subsystem. The registration starts either with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/SPring/Spring
/** | ||
* API to easily set up {@link GenericConverter} instances using Java 8 lambdas, mostly in bidirectional fashion for | ||
* easy registration as custom type converters of the SPring Data mapping subsystem. The registration starts either with | ||
* the definition of a reading or writing converter that can then be completed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add dot at the end.
|
||
/** | ||
* Creates a new {@link WritingConverterBuilder} to produce a converter to write values of the given source (the | ||
* domain type type) into the given target (the store type). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there's 2x type in domain type type
Typos in ConverterBuilder. Removed obsolete visibility modifiers for nested types in ConverterBuilder.
…va 8 lambdas. Introduced ConverterBuilder which exposes API to register Spring GenericConverters for the use with a store module's CustomConversions. This allows simple registration of such converters using Java 8 lambdas. ConverterAware converters = ConverterBuilder.reading(String.class, Long.class, it -> Long.valueOf(it)).andWriting(it -> Object::toString); The setup can also be done from the reading side which would just need the method invocations inverted. The resulting ConverterAware will expose the registered converters so that they can be easily handed to a CustomConversions instance. Partial creation of either reading or writing converters is possible, too with the returned instance then only exposing one of the two converters. CustomConversions now considers ConverterAware and treats them appropriately for ConvertiblePair registration as well as during the registration in the ConversionService. Tweaked parameter types in CustomConversions to rather accept a Collection<?> for the converters (previously List<?>). Also, registerConverterIn(…) now takes a ConverterRegistry over a GenericConversionService. Polished Javadoc and non-null assertions. Original pull request: #209.
Introduced
ConverterBuilder
which exposes API to register SpringGenericConverter
s for the use with a store module'sCustomConversions
. This allows simple registration of such converters using Java 8 lambdas.The setup can also be done from the reading side which would just need the method invocations inverted. The resulting
ConverterAware
will expose the registered converters so that they can be easily handed to aCustomConversions
instance. Partial creation of either reading or writing converters is possible, too with the returned instance then only exposing one of the two converters.